/*
 * sm4-1.0.js
 *
 * Copyright (c) 2019 RuXing Liang
 */
/**
 * @name sm4-1.0.js
 * @author RuXing Liang
 * @version 1.0.0 (2019-04-19)
 */
// var debug = false;

function SM4() {
  this.sbox = new Array(
    0xd6,
    0x90,
    0xe9,
    0xfe,
    0xcc,
    0xe1,
    0x3d,
    0xb7,
    0x16,
    0xb6,
    0x14,
    0xc2,
    0x28,
    0xfb,
    0x2c,
    0x05,
    0x2b,
    0x67,
    0x9a,
    0x76,
    0x2a,
    0xbe,
    0x04,
    0xc3,
    0xaa,
    0x44,
    0x13,
    0x26,
    0x49,
    0x86,
    0x06,
    0x99,
    0x9c,
    0x42,
    0x50,
    0xf4,
    0x91,
    0xef,
    0x98,
    0x7a,
    0x33,
    0x54,
    0x0b,
    0x43,
    0xed,
    0xcf,
    0xac,
    0x62,
    0xe4,
    0xb3,
    0x1c,
    0xa9,
    0xc9,
    0x08,
    0xe8,
    0x95,
    0x80,
    0xdf,
    0x94,
    0xfa,
    0x75,
    0x8f,
    0x3f,
    0xa6,
    0x47,
    0x07,
    0xa7,
    0xfc,
    0xf3,
    0x73,
    0x17,
    0xba,
    0x83,
    0x59,
    0x3c,
    0x19,
    0xe6,
    0x85,
    0x4f,
    0xa8,
    0x68,
    0x6b,
    0x81,
    0xb2,
    0x71,
    0x64,
    0xda,
    0x8b,
    0xf8,
    0xeb,
    0x0f,
    0x4b,
    0x70,
    0x56,
    0x9d,
    0x35,
    0x1e,
    0x24,
    0x0e,
    0x5e,
    0x63,
    0x58,
    0xd1,
    0xa2,
    0x25,
    0x22,
    0x7c,
    0x3b,
    0x01,
    0x21,
    0x78,
    0x87,
    0xd4,
    0x00,
    0x46,
    0x57,
    0x9f,
    0xd3,
    0x27,
    0x52,
    0x4c,
    0x36,
    0x02,
    0xe7,
    0xa0,
    0xc4,
    0xc8,
    0x9e,
    0xea,
    0xbf,
    0x8a,
    0xd2,
    0x40,
    0xc7,
    0x38,
    0xb5,
    0xa3,
    0xf7,
    0xf2,
    0xce,
    0xf9,
    0x61,
    0x15,
    0xa1,
    0xe0,
    0xae,
    0x5d,
    0xa4,
    0x9b,
    0x34,
    0x1a,
    0x55,
    0xad,
    0x93,
    0x32,
    0x30,
    0xf5,
    0x8c,
    0xb1,
    0xe3,
    0x1d,
    0xf6,
    0xe2,
    0x2e,
    0x82,
    0x66,
    0xca,
    0x60,
    0xc0,
    0x29,
    0x23,
    0xab,
    0x0d,
    0x53,
    0x4e,
    0x6f,
    0xd5,
    0xdb,
    0x37,
    0x45,
    0xde,
    0xfd,
    0x8e,
    0x2f,
    0x03,
    0xff,
    0x6a,
    0x72,
    0x6d,
    0x6c,
    0x5b,
    0x51,
    0x8d,
    0x1b,
    0xaf,
    0x92,
    0xbb,
    0xdd,
    0xbc,
    0x7f,
    0x11,
    0xd9,
    0x5c,
    0x41,
    0x1f,
    0x10,
    0x5a,
    0xd8,
    0x0a,
    0xc1,
    0x31,
    0x88,
    0xa5,
    0xcd,
    0x7b,
    0xbd,
    0x2d,
    0x74,
    0xd0,
    0x12,
    0xb8,
    0xe5,
    0xb4,
    0xb0,
    0x89,
    0x69,
    0x97,
    0x4a,
    0x0c,
    0x96,
    0x77,
    0x7e,
    0x65,
    0xb9,
    0xf1,
    0x09,
    0xc5,
    0x6e,
    0xc6,
    0x84,
    0x18,
    0xf0,
    0x7d,
    0xec,
    0x3a,
    0xdc,
    0x4d,
    0x20,
    0x79,
    0xee,
    0x5f,
    0x3e,
    0xd7,
    0xcb,
    0x39,
    0x48
  );

  this.fk = new Array(0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc);
  this.ck = new Array(
    0x00070e15,
    0x1c232a31,
    0x383f464d,
    0x545b6269,
    0x70777e85,
    0x8c939aa1,
    0xa8afb6bd,
    0xc4cbd2d9,
    0xe0e7eef5,
    0xfc030a11,
    0x181f262d,
    0x343b4249,
    0x50575e65,
    0x6c737a81,
    0x888f969d,
    0xa4abb2b9,
    0xc0c7ced5,
    0xdce3eaf1,
    0xf8ff060d,
    0x141b2229,
    0x30373e45,
    0x4c535a61,
    0x686f767d,
    0x848b9299,
    0xa0a7aeb5,
    0xbcc3cad1,
    0xd8dfe6ed,
    0xf4fb0209,
    0x10171e25,
    0x2c333a41,
    0x484f565d,
    0x646b7279
  );
}

SM4.prototype = {
  arrayCopy: function(src, pos1, dest, pos2, len) {
    var realLen = len;
    if (pos1 + len > src.length && pos2 + len <= dest.length) {
      realLen = src.length - pos1;
    } else if (pos2 + len > dest.length && pos1 + len <= src.length) {
      realLen = dest.length - pos2;
    } else if (pos1 + len <= src.length && pos2 + len <= dest.length) {
      realLen = len;
    } else if (dest.length < src.length) {
      realLen = dest.length - pos2;
    } else {
      realLen = src.length - pos2;
    }

    for (var i = 0; i < realLen; i++) {
      dest[i + pos2] = src[i + pos1];
    }
  },

  /*
   * int数转成byte数组
   * 事实上只不过转成byte大小的数，实际占用空间还是4字节
   * 返回：字节数组
   */
  intToByte: function(num) {
    return new Array(
      (num >> 24) & 0x000000ff,
      (num >> 16) & 0x000000ff,
      (num >> 8) & 0x000000ff,
      num & 0x000000ff
    );
  },

  /*
   * int数组转成byte数组，一个int数值转成四个byte
   * 返回:byte数组
   */
  intArrayToByteArray: function(nums) {
    var b = new Array(nums.length * 4);

    for (var i = 0; i < nums.length; i++) {
      this.arrayCopy(this.intToByte(nums[i]), 0, b, i * 4, 4);
    }

    return b;
  },

  /*
   * byte数组转成int数值
   * 返回：int数值
   */
  byteToInt: function(b, pos) {
    if (pos + 3 < b.length) {
      return (
        (b[pos] << 24) | (b[pos + 1] << 16) | (b[pos + 2] << 8) | b[pos + 3]
      );
    } else if (pos + 2 < b.length) {
      return (b[pos + 1] << 16) | (b[pos + 2] << 8) | b[pos + 3];
    } else if (pos + 1 < b.length) {
      return (b[pos] << 8) | b[pos + 1];
    } else {
      return b[pos];
    }
  },

  /*
   * byte数组转成int数组,每四个字节转成一个int数值
   *
   */
  byteArrayToIntArray: function(b) {
    // var arrLen = b.length%4==0 ? b.length/4:b.length/4+1;
    var arrLen = Math.ceil(b.length / 4); //向上取整
    var out = new Array(arrLen);
    for (var i = 0; i < b.length; i++) {
      b[i] = b[i] & 0xff; //避免负数造成影响
    }
    for (i = 0; i < out.length; i++) {
      out[i] = this.byteToInt(b, i * 4);
    }
    return out;
  },

  expandKey: function(key) {
    var k = new Array(36);
    var mk = this.byteArrayToIntArray(key);
    k[0] = mk[0] ^ this.fk[0];
    k[1] = mk[1] ^ this.fk[1];
    k[2] = mk[2] ^ this.fk[2];
    k[3] = mk[3] ^ this.fk[3];
    var rk = new Array(32);
    for (var i = 0; i < 32; i++) {
      k[i + 4] = k[i] ^ this.T1(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ this.ck[i]);
      rk[i] = k[i + 4];
    }
    return rk;
  },
  T1: function(ta) {
    // var rk = 0;
    var b = new Array(4);
    var a = this.intToByte(ta);
    b[0] = this.sbox[a[0] & 0xff];
    b[1] = this.sbox[a[1] & 0xff];
    b[2] = this.sbox[a[2] & 0xff];
    b[3] = this.sbox[a[3] & 0xff];
    var bint = this.byteToInt(b, 0);
    var rk =
      bint ^
      ((bint << 13) | (bint >>> (32 - 13))) ^
      ((bint << 23) | (bint >>> (32 - 23)));
    return rk;
  },
  one_encrypt: function(rk, data) {
    var x = new Array(36);
    x[0] = this.byteToInt(data, 0);
    x[1] = this.byteToInt(data, 4);
    x[2] = this.byteToInt(data, 8);
    x[3] = this.byteToInt(data, 12);
    let i;
    for (i = 0; i < 32; i++) {
      x[i + 4] = x[i] ^ this.T0(x[i + 1] ^ x[i + 2] ^ x[i + 3] ^ rk[i]);
    }
    var tmpx = new Array(4);
    for (i = 35; i >= 32; i--) {
      tmpx[35 - i] = x[i];
    }
    var xbyte = this.intArrayToByteArray(tmpx);

    return xbyte;
  },
  T0: function(ta) {
    var a = this.intToByte(ta);
    var b = new Array(4);
    b[0] = this.sbox[a[0] & 0xff];
    b[1] = this.sbox[a[1] & 0xff];
    b[2] = this.sbox[a[2] & 0xff];
    b[3] = this.sbox[a[3] & 0xff];
    var bint = this.byteToInt(b, 0);
    var c =
      bint ^
      ((bint << 2) | (bint >>> (32 - 2))) ^
      ((bint << 10) | (bint >>> (32 - 10))) ^
      ((bint << 18) | (bint >>> (32 - 18))) ^
      ((bint << 24) | (bint >>> (32 - 24)));
    return c;
  },
  pkcs7padding: function(input, mode) {
    if (input == null) {
      return null;
    }

    console.log(input.length + " " + input[input.length - 1]);

    var ret = null;
    let blockSize = 16;
    let p;
    if (mode == 1) {
      //填充
      p = blockSize - (input.length % blockSize);
      ret = new Array(input.length + p);
      this.arrayCopy(input, 0, ret, 0, input.length);
      for (var i = 0; i < p; i++) {
        ret[input.length + i] = p;
      }
    } //去除填充
    else {
      p = input[input.length - 1];
      console.log(input.length + "" + p);

      ret = new Array(input.length - p);
      this.arrayCopy(input, 0, ret, 0, input.length - p);
    }
    return ret;
  },
  ANSIX923Padding: function(input, mode) {
    if (input == null) {
      return null;
    }

    var ret = null;
    let blockSize = 16;

    let p;
    if (mode == 1) {
      //填充
      p = blockSize - (input.length % blockSize);
      ret = new Array(input.length + p);
      this.arrayCopy(input, 0, ret, 0, input.length);
      for (var i = 0; i < p - 1; i++) {
        ret[input.length + i] = 0x00;
      }
      ret[input.length + p - 1] = p;
    } //去除填充
    else {
      p = input[input.length - 1];
      ret = new Array(input.length - p);
      this.arrayCopy(input, 0, ret, 0, input.length - p);
    }
    return ret;
  },
  ISO10126Padding: function(input, mode) {
    if (input == null) {
      return null;
    }

    var ret = null;
    let blockSize = 16;

    if (mode == 1) {
      //填充
      let p = blockSize - (input.length % blockSize);
      ret = new Array(input.length + p);
      this.arrayCopy(input, 0, ret, 0, input.length);
      for (var i = 0; i < p - 1; i++) {
        var rand = Math.floor(Math.random() * 256); //可均衡获取0到255的随机整数。
        ret[input.length + i] = rand;
      }
      ret[input.length + p - 1] = p;
    } //去除填充
    else {
      let p = input[input.length - 1];
      ret = new Array(input.length - p);
      this.arrayCopy(input, 0, ret, 0, input.length - p);
    }
    return ret;
  },
  ZeroPadding: function(input, mode) {
    if (input == null) {
      return null;
    }

    var ret = null;
    let blockSize = 16;

    if (mode == 1) {
      //填充
      var p = blockSize - (input.length % blockSize);
      ret = new Array(input.length + p);
      this.arrayCopy(input, 0, ret, 0, input.length);
      for (let i = 0; i < p; i++) {
        ret[input.length + i] = 0x00;
      }
    } //去除填充
    else {
      var len = 0;
      for (let i = input.length - 1; i >= 0; i--) {
        if (input[i] != 0x00) break;
        len++;
      }

      ret = new Array(input.length - len);
      this.arrayCopy(input, 0, ret, 0, input.length - len);
    }
    return ret;
  },
  // paddingTypes 1: pkcs7padding 2:ANSIX923Padding 3:ISO10126Padding 4:ZeroPadding
  encrypt_ecb: function(key, data, paddingType) {
    console.log("encrypt_ecb");

    if (key == undefined || key == null || key.length % 16 != 0) {
      console.log("sm4 key is error!");
      return null;
    }
    if (data == undefined || data == null || data.length <= 0) {
      console.log("data is error!");
      return null;
    }
    var rk = this.expandKey(key);
    /*if(debug){
            var rkb = this.intArrayToByteArray(rk);
            console.log(Hex.encode(rkb,0,rkb.length));
        }*/

    console.log("encrypt_ecb");

    var blockLen = 16;
    var loop = parseInt(data.length / blockLen); //注意不能整除会有小数，要取整
    var cipher = new Array((loop + 1) * blockLen);
    var tmp = new Array(blockLen);
    var oneCipher = null;

    for (var i = 0; i < loop; i++) {
      this.arrayCopy(data, i * blockLen, tmp, 0, blockLen);
      oneCipher = this.one_encrypt(rk, tmp);
      this.arrayCopy(oneCipher, 0, cipher, i * blockLen, blockLen);
    }

    var lessData = new Array(data.length % blockLen);
    if (lessData.length > 0) {
      this.arrayCopy(
        data,
        loop * blockLen,
        lessData,
        0,
        data.length % blockLen
      );
    }
    var padding = null;

    // paddingTypes 1: pkcs7padding 2:ANSIX923Padding 3:ISO10126Padding 4:ZeroPadding
    switch (paddingType) {
      case 1:
        padding = this.pkcs7padding(lessData, 1);
        console.log("encrypt_ecb pkcs7padding");
        break;
      case 2:
        padding = this.ANSIX923Padding(lessData, 1);
        break;
      case 3:
        padding = this.ISO10126Padding(lessData, 1);
        break;
      case 4:
        padding = this.ZeroPadding(lessData, 1);
        break;
      default:
        padding = this.pkcs7padding(lessData, 1);
        break;
    }
    oneCipher = this.one_encrypt(rk, padding);
    this.arrayCopy(oneCipher, 0, cipher, loop * blockLen, blockLen);

    return cipher;
  },
  decrypt_ecb: function(key, data, paddingType) {
    if (key == undefined || key == null || key.length % 16 != 0) {
      console.log("sm4 key is error!");
      return null;
    }
    if (data == undefined || data == null || data.length % 16 != 0) {
      console.log("data is error!");
      return null;
    }
    var rk = this.expandKey(key);
    var nrk = new Array(32);
    let i;
    for (i = 0; i < rk.length; i++) {
      nrk[i] = rk[32 - i - 1];
    }
    /*if(debug){
            var rkb = this.intArrayToByteArray(rk);
            console.log(Hex.encode(rkb,0,rkb.length));
        }*/
    var blockLen = 16;
    var loop = data.length / blockLen - 1;
    var tmp = new Array(blockLen);
    var onePlain = null;
    var plain = null;
    //先解密最后一部分，确定数据长度
    this.arrayCopy(data, loop * blockLen, tmp, 0, blockLen);
    onePlain = this.one_encrypt(nrk, tmp);

    var lastPart = null;

    // paddingTypes 1: pkcs7padding 2:ANSIX923Padding 3:ISO10126Padding 4:ZeroPadding
    switch (paddingType) {
      case 1:
        lastPart = this.pkcs7padding(onePlain, 0);
        break;
      case 2:
        lastPart = this.ANSIX923Padding(onePlain, 0);
        break;
      case 3:
        lastPart = this.ISO10126Padding(onePlain, 0);
        break;
      case 4:
        lastPart = this.ZeroPadding(onePlain, 0);
        break;
      default:
        lastPart = this.pkcs7padding(onePlain, 0);
        break;
    }

    plain = new Array(loop * blockLen + lastPart.length);
    this.arrayCopy(lastPart, 0, plain, loop * blockLen, lastPart.length);

    //解密剩下部分数据
    for (i = 0; i < loop; i++) {
      this.arrayCopy(data, i * blockLen, tmp, 0, blockLen);
      onePlain = this.one_encrypt(nrk, tmp);
      this.arrayCopy(onePlain, 0, plain, i * blockLen, blockLen);
    }
    return plain;
  },
  encrypt_cbc: function(key, iv, data, paddingType) {
    if (key == undefined || key == null || key.length % 16 != 0) {
      console.log("sm4 key is error!");
      return null;
    }
    if (data == undefined || data == null || data.length <= 0) {
      console.log("data is error!");
      return null;
    }
    if (iv == undefined || iv == null || iv.length % 16 != 0) {
      console.log("iv is error!");
      return null;
    }
    var rk = this.expandKey(key);
    /*if(debug){
            var rkb = this.intArrayToByteArray(rk);
            console.log(Hex.encode(rkb,0,rkb.length));
        }*/

    var blockLen = 16;
    var loop = parseInt(data.length / blockLen); //注意不能整除会有小数，要取整
    var cipher = new Array((loop + 1) * blockLen);
    var tmp = new Array(blockLen);
    // var oneCipher = null;

    let i;
    for (i = 0; i < loop; i++) {
      this.arrayCopy(data, i * blockLen, tmp, 0, blockLen);
      for (var j = 0; j < blockLen; j++) {
        tmp[j] = tmp[j] ^ iv[j];
      }
      iv = this.one_encrypt(rk, tmp);
      this.arrayCopy(iv, 0, cipher, i * blockLen, blockLen);
    }

    var lessData = new Array(data.length % blockLen);
    if (lessData.length > 0) {
      this.arrayCopy(
        data,
        loop * blockLen,
        lessData,
        0,
        data.length % blockLen
      );
    }

    var padding = null;

    // paddingTypes 1: pkcs7padding 2:ANSIX923Padding 3:ISO10126Padding 4:ZeroPadding
    switch (paddingType) {
      case 1:
        padding = this.pkcs7padding(lessData, 1);
        console.log("encrypt_ecb pkcs7padding");
        break;
      case 2:
        padding = this.ANSIX923Padding(lessData, 1);
        break;
      case 3:
        padding = this.ISO10126Padding(lessData, 1);
        break;
      case 4:
        padding = this.ZeroPadding(lessData, 1);
        break;
      default:
        padding = this.pkcs7padding(lessData, 1);
        break;
    }

    for (i = 0; i < blockLen; i++) {
      padding[i] = padding[i] ^ iv[i];
    }
    iv = this.one_encrypt(rk, padding);
    this.arrayCopy(iv, 0, cipher, loop * blockLen, blockLen);

    return cipher;
  },
  decrypt_cbc: function(key, iv, data, paddingType) {
    if (key == undefined || key == null || key.length % 16 != 0) {
      console.log("sm4 key is error!");
      return null;
    }
    if (data == undefined || data == null || data.length % 16 != 0) {
      console.log("data is error!");
      return null;
    }
    if (iv == undefined || iv == null || iv.length % 16 != 0) {
      console.log("iv is error!");
      return null;
    }
    var rk = this.expandKey(key);
    var nrk = new Array(32);
    let i;
    for (i = 0; i < rk.length; i++) {
      nrk[i] = rk[32 - i - 1];
    }
    /*if(debug){
            var rkb = this.intArrayToByteArray(rk);
            console.log(Hex.encode(rkb,0,rkb.length));
        }*/
    var blockLen = 16;
    var loop = data.length / blockLen;
    var tmp = new Array(blockLen);
    var onePlain = null;
    var plain = null;

    //解密
    plain = new Array(data.length);
    for (i = 0; i < loop; i++) {
      this.arrayCopy(data, i * blockLen, tmp, 0, blockLen);
      onePlain = this.one_encrypt(nrk, tmp);
      for (var j = 0; j < blockLen; j++) {
        onePlain[j] = onePlain[j] ^ iv[j];
      }
      this.arrayCopy(tmp, 0, iv, 0, blockLen);
      this.arrayCopy(onePlain, 0, plain, i * blockLen, blockLen);
    }

    var lastPart = null;

    // paddingTypes 1: pkcs7padding 2:ANSIX923Padding 3:ISO10126Padding 4:ZeroPadding
    switch (paddingType) {
      case 1:
        lastPart = this.pkcs7padding(onePlain, 0);
        break;
      case 2:
        lastPart = this.ANSIX923Padding(onePlain, 0);
        break;
      case 3:
        lastPart = this.ISO10126Padding(onePlain, 0);
        break;
      case 4:
        lastPart = this.ZeroPadding(onePlain, 0);
        break;
      default:
        lastPart = this.pkcs7padding(onePlain, 0);
        break;
    }

    var realPlain = new Array(plain.length - blockLen + lastPart.length);
    this.arrayCopy(plain, 0, realPlain, 0, plain.length - blockLen);
    this.arrayCopy(
      lastPart,
      0,
      realPlain,
      plain.length - blockLen,
      lastPart.length
    );

    return realPlain;
  },
  encrypt_cfb: function(key, iv, data, paddingType) {
    if (key == undefined || key == null || key.length % 16 != 0) {
      console.log("sm4 key is error!");
      return null;
    }
    if (data == undefined || data == null || data.length <= 0) {
      console.log("data is error!");
      return null;
    }
    if (iv == undefined || iv == null || iv.length % 16 != 0) {
      console.log("iv is error!");
      return null;
    }
    var rk = this.expandKey(key);
    /*if(debug){
            var rkb = this.intArrayToByteArray(rk);
            console.log(Hex.encode(rkb,0,rkb.length));
        }*/

    var blockLen = 16;
    var loop = parseInt(data.length / blockLen); //注意不能整除会有小数，要取整
    var cipher = new Array((loop + 1) * blockLen);
    var tmp = new Array(blockLen);
    // var oneCipher = null;

    let i;
    for (i = 0; i < loop; i++) {
      this.arrayCopy(data, i * blockLen, tmp, 0, blockLen);

      //对上一次加密结果加密
      iv = this.one_encrypt(rk, iv);
      //与明文异或
      for (var j = 0; j < blockLen; j++) {
        iv[j] = tmp[j] ^ iv[j];
      }
      this.arrayCopy(iv, 0, cipher, i * blockLen, blockLen);
    }

    var lessData = new Array(data.length % blockLen);
    if (lessData.length > 0) {
      this.arrayCopy(
        data,
        loop * blockLen,
        lessData,
        0,
        data.length % blockLen
      );
    }

    var padding = null;

    // paddingTypes 1: pkcs7padding 2:ANSIX923Padding 3:ISO10126Padding 4:ZeroPadding
    switch (paddingType) {
      case 1:
        padding = this.pkcs7padding(lessData, 1);
        console.log("encrypt_ecb pkcs7padding");
        break;
      case 2:
        padding = this.ANSIX923Padding(lessData, 1);
        break;
      case 3:
        padding = this.ISO10126Padding(lessData, 1);
        break;
      case 4:
        padding = this.ZeroPadding(lessData, 1);
        break;
      default:
        padding = this.pkcs7padding(lessData, 1);
        break;
    }

    iv = this.one_encrypt(rk, iv);
    for (i = 0; i < blockLen; i++) {
      iv[i] = padding[i] ^ iv[i];
    }
    this.arrayCopy(iv, 0, cipher, loop * blockLen, blockLen);

    return cipher;
  },

  decrypt_cfb: function(key, iv, data, paddingType) {
    if (key == undefined || key == null || key.length % 16 != 0) {
      console.log("sm4 key is error!");
      return null;
    }
    if (data == undefined || data == null || data.length % 16 != 0) {
      console.log("data is error!");
      return null;
    }
    if (iv == undefined || iv == null || iv.length % 16 != 0) {
      console.log("iv is error!");
      return null;
    }
    var rk = this.expandKey(key);

    /*if(debug){
            var rkb = this.intArrayToByteArray(rk);
            console.log(Hex.encode(rkb,0,rkb.length));
        }*/
    var blockLen = 16;
    var loop = data.length / blockLen;
    var tmp = new Array(blockLen);
    var onePlain = null;
    var plain = null;

    //解密
    plain = new Array(data.length);
    let i;
    for (i = 0; i < loop; i++) {
      this.arrayCopy(data, i * blockLen, tmp, 0, blockLen);
      onePlain = this.one_encrypt(rk, iv);
      for (var j = 0; j < blockLen; j++) {
        onePlain[j] = onePlain[j] ^ tmp[j];
      }

      this.arrayCopy(tmp, 0, iv, 0, blockLen);
      this.arrayCopy(onePlain, 0, plain, i * blockLen, blockLen);
    }

    var lastPart = null;

    // paddingTypes 1: pkcs7padding 2:ANSIX923Padding 3:ISO10126Padding 4:ZeroPadding
    switch (paddingType) {
      case 1:
        console.log("decrypt_cfb pkcs7padding" + onePlain.length);

        lastPart = this.pkcs7padding(onePlain, 0);
        break;
      case 2:
        lastPart = this.ANSIX923Padding(onePlain, 0);
        break;
      case 3:
        lastPart = this.ISO10126Padding(onePlain, 0);
        break;
      case 4:
        lastPart = this.ZeroPadding(onePlain, 0);
        break;
      default:
        lastPart = this.pkcs7padding(onePlain, 0);
        break;
    }

    var realPlain = new Array(plain.length - blockLen + lastPart.length);
    this.arrayCopy(plain, 0, realPlain, 0, plain.length - blockLen);
    this.arrayCopy(
      lastPart,
      0,
      realPlain,
      plain.length - blockLen,
      lastPart.length
    );

    return realPlain;
  }
};
// module.exports = SM4;

export { SM4 };
